﻿using System;
using System.ComponentModel;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Threading;
using System.Xml;
using Microsoft.Practices.Unity;
using SharedLib;
using SharedLib.Helpers;
using SharedLib.WinFormMVCFramework;
using Tracy;
using Tracy.DataModel;
using Tracy.DataAccess;
using TracyGUI.View;
using Tracy.DataModel.SourceParser;
using Tracy.BasicServices;

namespace TracyGUI.Controller
{
    class EntryViewController : ControllerBase<IEntryView>, IController<IEntryView>
    {
        Entry _originalEntry = null;

        public Entry OriginalEntry
        {
            get { return _originalEntry; }
            private set { _originalEntry = value; }
        }


        protected override IEntryView ResolveView()
        {
            return Tracy.TracyFacade.Instance.DIContainer.Resolve<IEntryView>();
        }

        public EntryViewController(Entry entryToEdit)
        {
            //Init view
            _resultFound = new EventHandler<GenericEventArgs<SourceParserResult>>(this.Parser_OnResultFound);
            _parseCompleted = new EventHandler<GenericEventArgs<SourceEntryPair>>(this.Parser_OnParseCompleted);

            #region Monitor View Events
            View.EntryChanging += new EventHandler<GenericEventArgs<Entry>>(EntryView_EntryChanged);
            View.Search += new EventHandler<GenericEventArgs<Entry>>(EntryView_Search);
            View.FilterSetMatchedResultsShowing += new EventHandler<GenericEventArgs<FilterSet>>(EntryView_ShowFilterSetMatchedResults);
            View.FilterSetContextMenuOpening += new EventHandler<EnhancedEventArgs>(EntryView_FilterSetContextMenuOpening);
            #endregion

            if (entryToEdit != null) //edit mode
            {
                OriginalEntry = entryToEdit;
                View.LoadEntry((Entry)entryToEdit.Clone());
                View.LoadFilterSets(TracyFacade.Instance.FilterManager.FilterProvider.GetFilterSets(entryToEdit), null, null, null);
            }
            else //creation mode
            {
                View.PrepareForEntryCreation();
            }

            View.ApplyLanguage(Tracy.TracyFacade.Instance.LanguageManager);
        }

        #region View Events
        private void RememberLastSavePath(string savePath)
        {
            if (!String.IsNullOrEmpty(savePath) && savePath != TracyFacade.Instance.UserPreferencesManager.SavePath)
            {
                int index = savePath.TrimEnd('\\').LastIndexOf('\\');
                if (index > 0) savePath = savePath.Remove(index);
                UserPreferencesManager.UserPreferences pref = TracyFacade.Instance.UserPreferencesManager.CurrentUserPreferences;
                pref.SavePath = savePath;
                TracyFacade.Instance.UserPreferencesManager.Update(pref);
            }
        }

        void EntryView_EntryChanged(object sender, GenericEventArgs<Entry> e)
        {
            if (View.ModalResult)
            {
                Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
                {
                    if (OriginalEntry == null)
                    {
                        Tracy.TracyFacade.Instance.EntryProvider.AddItem(e.Data);
                    }
                    else
                    {
                        Tracy.TracyFacade.Instance.EntryProvider.UpdateItem(OriginalEntry, e.Data);
                    }
                    TracyFacade.Instance.FilterManager.FilterProvider.SetFilterSets(View.SelectedFilterSets, e.Data);
                    RememberLastSavePath(e.Data.SavePath);
                }, "UI Policy");
                OriginalEntry = e.Data;
            }
        }

        void EntryView_Search(object sender, GenericEventArgs<Entry> e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                if (OriginalEntry == null) //creation mode
                {
                    e.Data = new Entry(e.Data.SearchKeywords);
                    e.Data.SavePath = TracyFacade.Instance.UserPreferencesManager.SavePath + StringHelper.RemoveInvalidCharFromFileName(e.Data.Name);
                    View.RefreshEntry(e.Data);
                }
                PerformSearch(e.Data);
            }, "UI Policy");
        }

        void EntryView_FilterSetContextMenuOpening(object sender, EnhancedEventArgs e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                if (e != null && e.Attributes.ContainsKey("filter-set") && e.Attributes.ContainsKey("cancel-event-args"))
                {
                    FilterSet fs = (FilterSet)e.Attributes["filter-set"];
                    if (_matchedResources != null && _matchedResources.ContainsKey(fs) && _matchedResources[fs].Count > 0)
                    {
                        ((CancelEventArgs)e.Attributes["cancel-event-args"]).Cancel = false;
                    }
                }
            }, "UI Policy");
        }

        void EntryView_ShowFilterSetMatchedResults(object sender, GenericEventArgs<FilterSet> e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                FilterSet fs = e.Data;
                if (_matchedResources != null && _matchedResources.ContainsKey(fs))
                {
                    StringWriter sw = new StringWriter();
                    foreach (string resTitle in _matchedResources[fs])
                    {
                        sw.WriteLine(resTitle);
                    }
                    MessageBox.Show(sw.ToString(), TracyFacade.Instance.LanguageManager.GetText("MessageBox_MatchedResources_Title"));
                }
            }, "UI Policy");
        }
        #endregion

        #region Search
        private int _parseTaskCount = 0;
        private object _lockHelper = new object();
        EventHandler<GenericEventArgs<SourceParserResult>> _resultFound;
        EventHandler<GenericEventArgs<SourceEntryPair>> _parseCompleted;
        List<FilterSet> _displayedFilterSets = new List<FilterSet>();
        private bool _isFirstResult = true;
        FormInProgress _formInProgress;
        private void PerformSearch(Entry entry)
        {
            //Init search
            _displayedFilterSets.Clear();
            foreach (FilterSet fs in View.SelectedFilterSets) _displayedFilterSets.Add(fs);
            _isFirstResult = true;

            foreach (FeedSource source in TracyFacade.Instance.FilterManager.FeedSourceProvider.GetItems)
            {
                ISourceParser parser = TracyFacade.Instance.DIContainer.Resolve<ISourceParser>(source.Parser);
                parser.OnResultFound += _resultFound;
                parser.OnParseCompleted += _parseCompleted;
                parser.Parse(source, entry);//TODO: Report Parse progress
                Interlocked.Increment(ref _parseTaskCount);
            }
            _formInProgress = new FormInProgress(TracyFacade.Instance.LanguageManager.GetText("InProgressFormTitle"), 
                TracyFacade.Instance.LanguageManager.GetText("InProgressFormMessage"));
            _formInProgress.ShowDialog();
        }

        Dictionary<FilterSet, List<string>> _matchedResources = null;
        void Parser_OnResultFound(object sender, GenericEventArgs<SourceParserResult> e)
        {
            lock (_lockHelper)
            {
                //Validate if user cancels the task
                if (_formInProgress == null) return;
                //Hide InProgress Form if no download task is active

                if (_formInProgress.DialogResult == System.Windows.Forms.DialogResult.Cancel) return;

                //Result from parser
                FeedSource source = e.Data.Source;
                XmlDocument doc = e.Data.ResultDoc;

                //Clear previous result if this is the first result returned
                if (_isFirstResult)
                {
                    _isFirstResult = false;
                    _matchedResources = new Dictionary<FilterSet, List<string>>();
                }

                bool hasNewMatchedItem = false;
                Regex regEx = new Regex(e.Data.Entry.RegExpr.ToUpper());

                foreach (XmlElement resourceNode in doc.SelectNodes("result/resource"))
                {
                    string title = XmlHelper.GetNodeValue(resourceNode, "title");
                    string titleToFilter = StringHelper.UnifyStringFormat(title);

                    if (regEx.Match(titleToFilter).Success)//match entry
                    {

                        //Parse FanSubGroup
                        List<FanSubGroup> resourceFanSubGroups = FilterProvider.GetFanSubGroups(titleToFilter);
                        if (resourceFanSubGroups.Count > 0)
                        {
                            //Parse Format
                            Format resourceFormat = FilterProvider.GetFormat(titleToFilter);
                            if (resourceFormat != null)
                            {
                                //Parse Resolution
                                Resolution resourceResolution = FilterProvider.GetResolution(titleToFilter);
                                if (resourceResolution != null)
                                {
                                    hasNewMatchedItem = true;
                                    //Build FilterSet
                                    FilterSet resourceFilterSet = new FilterSet();
                                    foreach (FanSubGroup fanSubGroup in resourceFanSubGroups)
                                        resourceFilterSet.FanSubGroups.Add(fanSubGroup);

                                    resourceFilterSet.Format = resourceFormat;
                                    resourceFilterSet.Resolution = resourceResolution;
                                    resourceFilterSet.FeedSource = source;

                                    int index = -1;
                                    for (int i = 0; i < _displayedFilterSets.Count; i++)
                                    {
                                        if (_displayedFilterSets[i].Equals(resourceFilterSet))
                                        {
                                            index = i;
                                            break;
                                        }
                                    }

                                    if (index >= 0)
                                    {
                                        //Map to Existing FilterSet
                                        FilterSet existingFilterSet = _displayedFilterSets[index];
                                        if (_matchedResources.ContainsKey(existingFilterSet))
                                        {
                                            //Already cached
                                            _matchedResources[existingFilterSet].Add(titleToFilter);
                                        }
                                        else
                                        {
                                            //Not cached yet
                                            AppendFilterSetToMatchedResources(_matchedResources, titleToFilter, existingFilterSet);
                                        }
                                    }
                                    else
                                    {
                                        //New FilterSet found
                                        _displayedFilterSets.Add(resourceFilterSet);
                                        AppendFilterSetToMatchedResources(_matchedResources, titleToFilter, resourceFilterSet);
                                    }
                                }
                            }
                        }
                    }
                }
                if (hasNewMatchedItem)
                {
                    RefreshSearchResult(_matchedResources);
                }
            }
        }

        private void RefreshSearchResult(Dictionary<FilterSet, List<string>> matchedResources)
        {
            List<FilterSet> filterSets = matchedResources.Keys.ToList();
            List<bool> selectedList = new List<bool>();
            List<string> countList = new List<string>();
            List<string> exampleList = new List<string>();
            foreach (FilterSet fs in filterSets)
            {
                selectedList.Add(View.SelectedFilterSets.Contains(fs));
                List<string> matchList = matchedResources[fs];
                countList.Add(matchList.Count.ToString());
                exampleList.Add(matchList[0]);
            }
            View.LoadFilterSets(filterSets, selectedList, countList, exampleList);
        }

        private static void AppendFilterSetToMatchedResources(Dictionary<FilterSet, List<string>> matchedResources, string titleToFilter, FilterSet resourceFilterSet)
        {
            List<string> matchedTitles = new List<string>();
            matchedTitles.Add(titleToFilter);
            matchedResources.Add(resourceFilterSet, matchedTitles);
        }

        void Parser_OnParseCompleted(object sender, GenericEventArgs<SourceEntryPair> e)
        {
            lock (_lockHelper)
            {
                int currentParseTaskCount = Interlocked.Decrement(ref _parseTaskCount);
                if (currentParseTaskCount <= 0)
                {
                    _formInProgress.HideForm();
                }
                if (sender is ISourceParser)
                {
                    ((ISourceParser)sender).OnResultFound -= _resultFound;
                    ((ISourceParser)sender).OnParseCompleted -= _parseCompleted;
                }
            }
        }
        #endregion
    }
}
